-module(prj2).
-export([init/3, han_init/3]).
-import(erlang, [display/1]).

han_init(NumServers, FileDir, Replication) ->

    	random:seed(now()),
	
	Proc1 = {prj2server, 'inst1@192.168.0.101'},
	
        Proc1 ! {start, NumServers+2, self()},
	Servers = getMessage(),
	
	try
		db:initNeighbors(Servers, Servers),

		FileList = getFilesFromDirectory(FileDir),
		sendFilesToServers(FileList, Servers, Replication),

		db:setMinForEachProcess(Servers),
		db:getGossipMin(Servers, length(Servers)),
		db:print(Servers),
		db:stopServers(Servers)
	catch
		Error ->
			display(Error),
			db:stopServers(Servers)
	end,
    ok.

init(NumServers, FileDir, Replication) ->

    random:seed(now()),
	
	Proc1 = {prj2server, 'inst1@192.168.0.101'},
	Proc2 = {prj2server, 'inst2@192.168.0.102'},
    	
	
        Proc1 ! {start, NumServers, self()},
	Proc2 ! {start, NumServers, self()},
	Servers = getMessage() ++ getMessage(),
	
	try
		FileList = getFilesFromDirectory(FileDir),
		sendFilesToServers(FileList, Servers, Replication),
	
	    Min = db:getMin(Servers),
	    Max = db:getMax(Servers),
	    io:format("Min:\t~p~n", [Min]),
	    io:format("Max:\t~p~n", [Max]),	

		db:stopServers(Servers)
	catch
		Error ->
			display(Error),
			db:stopServers(Servers)
	end,
    ok.

% Gets a single response from the mailbox
getMessage() ->
	receive
		Msg ->
			Msg
	end.

%% Distributes the given list of files to a random set of Servers with the given Replication
sendFilesToServers([], _Servers, _Replication) ->
    ok;
sendFilesToServers(FileList, Servers, Replication) ->
    % Get the first file in the list
    % Get random servers that will be holding this server
    ServersToHoldFile = getRandomServers(Servers, Replication, []),   
    File = file:open(hd(FileList), read),
    case File of
        {ok, IoDevice} ->
            Floats = readFloatsFromFile(IoDevice, []),
            sendFloatsToServers(getTagFromFilename(hd(FileList)), Floats, ServersToHoldFile),
            sendFilesToServers(tl(FileList), Servers, Replication);
        {error, eisdir} ->
            sendFilesToServers(tl(FileList), Servers, Replication);            
        {error, Msg} ->
            throw({error, Msg})
    end,
    ok.

% Reads all the floats from the given file until EOF
readFloatsFromFile(IoDevice, ListOfFloats) ->
    Line = file:read_line(IoDevice),
    case Line of
        {ok, Data} ->
            readFloatsFromFile(IoDevice, [list_to_float(re:replace(Data, "\\s$", "", [global, {return, list}])) | ListOfFloats ]);
        eof ->
            ListOfFloats
    end.

%% Sends a list of floats to the set of lines
sendFloatsToServers(_Tag, _Floats, []) ->
    ok;
sendFloatsToServers(Tag, Floats, Servers) ->
    db:add(hd(Servers), Tag, Floats),
    sendFloatsToServers(Tag, Floats, tl(Servers)).
    
% Gets a list of random servers   
getRandomServers(_FullServerList, Number, Add) when length(Add) == Number ->
    Add;
getRandomServers(FullServerList, Number, Add) ->
    Index = random:uniform(length(FullServerList)),
    getRandomServers(FullServerList, Number, lists:usort([ lists:nth(Index, FullServerList) | Add ])).

%% Gets a listing of files from the given directory
getFilesFromDirectory(FileDir) when is_list(FileDir) ->
    Filenames = file:list_dir(FileDir),
    if
        element(1, Filenames) /= ok ->
            throw({error, "Error getting files from directory"});
        true ->
            addDirToFiles(FileDir, lists:sort(element(2, Filenames)), [])
    end;
getFilesFromDirectory(_FileDir) ->
    throw({exit, "FileDir must be a string"}).

%% Adds the directory string to each of the files
addDirToFiles(_FileDir, [], Add) ->
    Add;
addDirToFiles(FileDir, Filenames, Add) ->
    addDirToFiles(FileDir, tl(Filenames), [ FileDir ++ "/" ++ hd(Filenames) | Add]).

%% Gets the tag integer from a filename, expected format ".*-[0-9].txt"
getTagFromFilename(Filename) ->
    list_to_integer(re:replace(re:replace(Filename, ".*-", "", [global, {return, list}]), "\.txt", "", [global, {return, list}])).
